#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "Common.h"
#include "Window.h"
#include "Internals.h"
#include "CtrlClass.h"

#include "Assert.h"

/* current active main window handle */
PMAINWIN __lg_active_mainwnd;

/* pointer to desktop message queue */
PMSGQUEUE __lg_dsk_msg_queue;

HWND __lg_hwnd_desktop;                       /* handle of desktop window */
WNDPROC __lg_def_proc[2];

static ZORDERINFO sg_MainWinZOrder;

static void InitZOrderInfo (PZORDERINFO pZOrderInfo, HWND hHost)
{
    pZOrderInfo->nNumber = 0;
    pZOrderInfo->hWnd = hHost;
    pZOrderInfo->pTopMost = NULL;
}

static ZORDERINFO* GET_WIN_ZORDER (PMAINWIN pWin)
{
    return &sg_MainWinZOrder;
}


MSGQUEUE* InitMsgQueueThisThread (void)
{
    MSGQUEUE* pMsgQueue;
    int ret;

    if (!(pMsgQueue = malloc(sizeof(MSGQUEUE))) ) 
    {
        return NULL;
    }

    if (!InitMsgQueue(pMsgQueue, 0)) 
    {
        free (pMsgQueue);
        return NULL;
    }

    return pMsgQueue;
}

void FreeMsgQueueThisThread (void)
{
    MSGQUEUE* pMsgQueue;

    if (pMsgQueue) 
    {
        DestroyMsgQueue (pMsgQueue);
        free (pMsgQueue);
    }
}

BOOL InitDesktop(void)
{
    static MAINWIN sg_desktop_win;
    PMAINWIN pDesktopWin;

    pDesktopWin = &sg_desktop_win;

    /* init message queue of desktop thread */
    if (!(__lg_dsk_msg_queue = InitMsgQueueThisThread ()) ) {
        return FALSE;
    }

    pDesktopWin->pMessages         = __lg_dsk_msg_queue;
    pDesktopWin->MainWindowProc    = DesktopWinProc;
    pDesktopWin->DataType          = TYPE_HWND;
    pDesktopWin->WinType           = TYPE_ROOTWIN;
    pDesktopWin->spCaption         = "THE DESKTOP WINDOW";

    pDesktopWin->pMainWin          = pDesktopWin;
    __lg_hwnd_desktop = (HWND)pDesktopWin;

    /*
     * Init ZOrderInfo here.
     */
    InitZOrderInfo (&sg_MainWinZOrder, HWND_DESKTOP);

    return TRUE;
}

void TerminateDesktop (void)
{
    if (__lg_dsk_msg_queue) 
    {
        DestroyMsgQueue (__lg_dsk_msg_queue);
        free (__lg_dsk_msg_queue);
        __lg_dsk_msg_queue = NULL;
    }
}

static void dskOnNewCtrlInstance (PCONTROL pParent, PCONTROL pNewCtrl)
{
    PCONTROL pFirstCtrl, pLastCtrl;

    pFirstCtrl = pParent->children;

    pNewCtrl->next = NULL;

    if (!pFirstCtrl) 
    {
        pParent->children = pNewCtrl;
        pNewCtrl->prev = NULL;
    }
    else 
    {
        pLastCtrl = pFirstCtrl;

        while (pLastCtrl->next)
        {
            pLastCtrl = pLastCtrl->next;
        }

        pLastCtrl->next = pNewCtrl;
        pNewCtrl->prev = pLastCtrl;
    }

    pNewCtrl->pcci->nUseCount ++;
}

static BOOL dskOnRemoveCtrlInstance (PCONTROL pParent, PCONTROL pCtrl)
{
    PCONTROL pFirstCtrl;
    BOOL fFound = FALSE;

    pFirstCtrl = pParent->children;

    if (!pFirstCtrl)
        return FALSE;
    else {
        if (pFirstCtrl == pCtrl) 
        {
            pParent->children = pCtrl->next;
            if (pCtrl->next)
                pCtrl->next->prev = NULL;
            fFound = TRUE;
        }
        else 
        {
            while (pFirstCtrl->next) 
            {
                if (pFirstCtrl->next == pCtrl) 
                {
                    pFirstCtrl->next = pCtrl->next;
                    if (pFirstCtrl->next)
                    {
                        pFirstCtrl->next->prev = pCtrl->prev;
                    }
                    fFound = TRUE;
                    break;
                }

                pFirstCtrl = pFirstCtrl->next;
            }
        }
    }

    if (fFound)
        pCtrl->pcci->nUseCount --;

    return fFound;
}

static PMAINWIN dskGetFirstActivableWindow (void)
{
    PZORDERNODE pNode;
    PMAINWIN pTemp;

    pNode = sg_MainWinZOrder.pTopMost;
    while (pNode) 
    {

        pTemp = (PMAINWIN)(pNode->hWnd);

        if ((pTemp->WinType != TYPE_CONTROL)
                && (pTemp->dwStyle & WS_VISIBLE)
                && !(pTemp->dwStyle & WS_DISABLED))
        {
            return pTemp;
        }

        pNode = pNode->pNext;
    }

    return NULL;
}

static void add_new_window (PMAINWIN pWin, PZORDERNODE pNode)
{
    ZORDERINFO *zorder;

    zorder = GET_WIN_ZORDER(pWin);

    pNode->pNext = zorder->pTopMost;
    zorder->pTopMost = pNode;
    zorder->nNumber++;
}

static void remove_window (PMAINWIN pWin)
{
    PZORDERNODE pNode, pTemp;
    ZORDERINFO *zorder;

    zorder = GET_WIN_ZORDER(pWin);

    pNode = zorder->pTopMost;
    if (pNode->hWnd == (HWND)pWin) 
    {
        zorder->pTopMost = pNode->pNext;
    }
    else 
    {
        while (pNode->pNext) 
        {
            if (pNode->pNext->hWnd == (HWND)pWin) 
            {
                pTemp = pNode->pNext->pNext;
                pNode->pNext = pTemp;

                break;
            }
            pNode = pNode->pNext;
        }
    }
    zorder->nNumber--;
}

// this funciton add the new main window to the main window list.
// if new main window is a visible window,
// this new main window becomes the active window and this function
// return the old main window.
// otherwise, return NULL.
static PMAINWIN dskAddNewMainWindow (PMAINWIN pWin, PZORDERNODE pNode)
{
    LG_ASSERT(0);
    // Update Z Order info.
    pNode->hWnd = (HWND)pWin;
    add_new_window (pWin, pNode);

    LG_ASSERT(0);
    // show and active this main window.
    if ( pWin->dwStyle & WS_VISIBLE )
    {
        LG_ASSERT(0);
        __lg_active_mainwnd = pWin;
    }

}

// When destroy a main win, all main win which is covered by
// this destroying main win will be redraw.
//
// Functions which lead to call this function:
//  DestroyWindow: destroy a visible window.
//
// return: the new active window.
static PMAINWIN dskRemoveMainWindow (PMAINWIN pWin)
{
    PCONTROL pCtrl, pNext;

    // Update all affected windows' GCR info.
    if (pWin->dwStyle & WS_VISIBLE)
    {
        pWin->dwStyle &= ~WS_VISIBLE;

        if (__lg_active_mainwnd == pWin)
        {
            __lg_active_mainwnd = dskGetFirstActivableWindow();
        }

        if (__lg_active_mainwnd == pWin)
        {
            __lg_active_mainwnd = NULL;
        } 
        
		if(__lg_active_mainwnd)
        {
            LG_ASSERT(0);
            SendMessage ((HWND)__lg_active_mainwnd, MSG_PAINT, 0, 0);

            pCtrl = (PCONTROL)(__lg_active_mainwnd->hFirstChild);
            while (pCtrl) 
            {
                pNext = pCtrl->next;
                LG_ASSERT(0);
                SendMessage ((HWND)pCtrl, MSG_PAINT, 0, 0);
                pCtrl = pNext;
            }
        }
    }

    // Update window Z order list.
    remove_window (pWin);

    return __lg_active_mainwnd;
}

int DesktopWinProc (HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
{
    int iRet = 0;

    LG_ASSERT(0);
    
    if( __lg_active_mainwnd  &&( message == MSG_KEYDOWN))
    {
        PostMessage((HWND)__lg_active_mainwnd, message, wParam, lParam);
        LG_ASSERT(0);
    }

    switch(message)
    {
        case MSG_NEWCTRLINSTANCE:
            dskOnNewCtrlInstance ((PCONTROL)wParam, (PCONTROL)lParam);
        break;
        case MSG_REMOVECTRLINSTANCE:
            if (!dskOnRemoveCtrlInstance ((PCONTROL)wParam, (PCONTROL)lParam))
            {
                return -1;
            }
        break;
        case MSG_GETACTIVEMAIN:
            return (int)__lg_active_mainwnd;        
        case MSG_GETCTRLCLASSINFO:
            //Uart0_Printf("MSG_GETCTRLCLASSINFO %s\r\n", (const char*)lParam);
        return (int)GetControlClassInfo ((const char*)lParam);
        break;
        case MSG_ADDNEWMAINWIN:
            iRet = (int)dskAddNewMainWindow((PMAINWIN)wParam, (PZORDERNODE)lParam);
        break;
        case MSG_REMOVEMAINWIN:
            iRet = (int)dskRemoveMainWindow((PMAINWIN)wParam);
        break;
    }

    return 0;
}
